import type { ILayer, LayerPopup, MarkerLayer } from "@antv/l7";

/**
 * 图层类型，影响堆叠顺序
 * base: 底图图层 -100-0
 * default: 默认图层 0-1000
 * activity: 活动图层 1000-1100
 */
export type LayerType = "base" | "default" | "activity";

/**
 * zIndex 范围
 */
const ZINDEX_SCOPE = {
    base: [-100, 0],
    default: [0, 1000],
    activity: [1000, 1100]
};

/**
 * 图层配置
 */
export interface LayerConfig {
    /** 图层 id */
    id?: string;
    /** 图层标题 */
    title: string;
    /** 图层类型 */
    type: LayerType;
    /** 图层描述 */
    describe: string;
    /** 图层可用性 */
    available?: boolean;
    /** 图层可见性 */
    visible?: boolean;
    /** 图层 zIndex */
    zIndex?: number;
    /** 创建时间 */
    createTime?: Date;
}

/**
 * 图层状态
 */
export interface LayerState extends LayerConfig {
    /** 图层 id */
    id: string;
    /** 图层可用性 */
    available: boolean;
    /** 图层可见性 */
    visible: boolean;
    /** 图层是否初始化 */
    inited: boolean;
    /** 图层 zIndex */
    zIndex?: number;
    /** bbox */
    bbox?: [[number, number], [number, number]];
    /** 创建时间 */
    createTime: Date;
}

/**
 * 图层各实例
 */
export interface MapLayerInstances {
    /** 图层实例 */
    layer?: ILayer;
    /** 图层弹窗实例 */
    layerPopup?: LayerPopup;
    /** 标记图层实例 */
    markerLayer?: MarkerLayer;
}

/**
 * 图层创建器
 */
export type LayerCreator<T extends MapLayerInstances> = () => Promise<T>;
/**
 * 地图图层
 */
export class MapLayer<O extends MapLayerInstances = MapLayerInstances> implements LayerState {
    readonly id: string;
    title: string;
    type: LayerType;
    describe: string;
    visible: boolean;
    available: boolean;
    zIndex: number;
    createTime: Date;

    inited: boolean = false;
    bbox?: [[number, number], [number, number]] = undefined;

    protected creator: LayerCreator<O>;
    /** 图层实例 */
    protected instances: O = {} as O;

    /**
     * @param id      图层 id
     * @param config  图层配置
     * @param creator 图层创建器
     * @returns       地图图层实例
     */
    constructor(config: LayerConfig, creator: LayerCreator<O>) {
        this.id = config.id ?? Math.random().toString(36).substring(2, 9);
        this.title = config.title;
        this.type = config.type;
        this.describe = config.describe;
        this.visible = config.visible ?? true;
        this.available = config.available ?? true;
        this.zIndex = this.generateIndex(config.type, config.zIndex);
        this.createTime = config.createTime || new Date();
        this.creator = creator;
        return this;
    }
    /**
     * 生成 zIndex
     */
    protected generateIndex(layerType: LayerType, zIndex?: number): number {
        const [min, max] = ZINDEX_SCOPE[layerType];
        if (!zIndex) {
            return min;
        }
        if (zIndex < min) {
            throw new Error(`zIndex must < ${min}`);
        }
        if (zIndex >= max) {
            throw new Error(`zIndex must >= ${max}`);
        }
        return zIndex;
    }

    /*------------------------------------------------------*/
    /* 基本方法 */

    /**
     * 获取各实例
     */
    getInstances(): O {
        const instances = this.tryGetInstances();
        if (!instances) {
            throw new Error("Layer not initialized");
        }
        return instances;
    }
    /**
     * 尝试获取各实例
     */
    tryGetInstances(): O | null {
        if (!this.inited || !this.instances) {
            return null;
        }
        return this.instances;
    }
    /**
     * 初始化
     * @returns 各实例
     */
    async init(): Promise<O> {
        if (this.inited) {
            return this.getInstances();
        }
        const results = await this.creator().catch((error) => {
            throw new Error(`Layer init failed: ${error.message}`);
        });
        const promises1 = new Promise((resolve) => {
            this.instances.layer?.on("inited", () => {
                this.setLayerIndex(this.zIndex);
                this.setVisible(this.visible);
                return resolve(this.instances.layer);
            });
        });
        const promises2 = new Promise((resolve) => {
            this.instances.layerPopup?.on("inited", () => {
                return resolve(this.instances.layerPopup);
            });
        });
        await Promise.all([promises1, promises2]).catch((error) => {
            throw new Error(`Layer init failed: ${error.message}`);
        });
        this.instances = results;
        this.inited = true;
        return this.instances;
    }
    /**
     * 销毁
     * @returns 地图图层实例
     */
    destroy(): MapLayer {
        if (this.instances.layer) {
            this.instances.layer.destroy(true);
            this.instances.layer = undefined;
        }
        if (this.instances.layerPopup) {
            this.instances.layerPopup.remove();
            this.instances.layerPopup = undefined;
        }
        if (this.instances.markerLayer) {
            this.instances.markerLayer.destroy();
            this.instances.markerLayer = undefined;
        }
        this.inited = false;
        this.bbox = undefined;
        this.visible = false;
        this.available = false;
        return this;
    }
    /**
     * 获取状态
     * @returns 状态
     */
    getState(): LayerState {
        return {
            id: this.id,
            title: this.title,
            type: this.type,
            describe: this.describe,
            visible: this.visible,
            available: this.available,
            zIndex: this.zIndex,
            inited: this.inited,
            createTime: this.createTime
        };
    }
    /**
     * 设置图层可见性
     * @param visible 图层可见性
     * @returns 地图图层实例
     */
    setVisible(visible: boolean): MapLayer {
        const instances = this.tryGetInstances();
        if (instances) {
            if (visible) {
                instances.layer?.show();
                instances.layerPopup?.open();
                instances.markerLayer?.show();
            } else {
                instances.layer?.hide();
                instances.layerPopup?.close();
                instances.markerLayer?.hide();
            }
        }
        this.visible = visible;
        return this;
    }
    /**
     * 设置图层可用性
     * @param available 图层可用性
     * @returns 地图图层实例
     */
    setAvailable(available: boolean): MapLayer {
        this.available = available;
        return this;
    }

    /*------------------------------------------------------*/
    /* layer 方法 */

    /**
     * 获取图层实例 如果不存在则抛出异常
     * @returns 图层实例
     */
    getLayerInstance(): ILayer {
        const layer = this.tryGetLayerInstance();
        if (!layer) {
            throw new Error("Layer not initialized");
        }
        return layer;
    }
    /**
     * 尝试获取图层实例
     * @returns 图层实例
     */
    tryGetLayerInstance(): ILayer | undefined {
        return this.instances.layer;
    }
    /**
     * 设置图层 zIndex
     * @param zIndex 图层 zIndex
     * @returns 地图图层实例
     */
    setLayerIndex(zIndex: number): MapLayer {
        const index = this.generateIndex(this.type, zIndex);
        this.zIndex = index;
        this.getLayerInstance().setIndex(index);
        return this;
    }
    /**
     * 获取图层 zIndex
     * @returns 图层 zIndex
     */
    getLayerIndex(): number {
        return this.zIndex;
    }
}

/**
 * GeoJson 图层各实例
 */
export interface GeoJsonLayerInstances extends MapLayerInstances {
    data: GeoJSON.FeatureCollection;
}

/**
 * GeoJson 图层
 */
export class GeoJsonMapLayer extends MapLayer<GeoJsonLayerInstances> {
    data?: GeoJSON.FeatureCollection;

    /**
     * @param config  图层配置
     * @param creator 图层创建器
     * @param data    GeoJSON 数据
     * @returns       地图图层实例
     */
    constructor(config: LayerConfig, creator: LayerCreator<GeoJsonLayerInstances>) {
        super(config, creator);
        this.creator = creator;
        return this;
    }
    /**
     * 初始化图层
     * @param callback 图层初始化回调
     * @returns 地图图层实例
     */
    async init() {
        if (this.inited) {
            return this.getInstances();
        }
        const results = await super.init();
        this.data = results.data;
        return results;
    }
    /**
     * 设置 GeoJson 数据
     * @param data GeoJson 数据
     * @returns 地图图层实例
     */
    setData(data: GeoJSON.FeatureCollection): GeoJsonMapLayer {
        this.data = data;
        this.getLayerInstance().setData(data);
        return this;
    }
    /**
     * 获取 GeoJson 数据
     * @returns GeoJson 数据
     */
    getData(): GeoJSON.FeatureCollection {
        if (!this.data) throw new Error("GeoJson data not set");
        return this.data;
    }
}
